package org.jugile.util;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;


public class PropNode extends Jugile {
	
	private String name;
	public String getName() { return name; }
	public void setName(String v) { name = v; }
	
	private int indent = -1;
	
	private String desc;
	public String getDesc() { return desc; }
	private void initDesc() {
		if (desc != null) addDescLine("--------------------------");
	}
	private void addDescLine(String v) {
		if (desc == null) desc = v;
		else desc = desc + "\n" + v;
	}
	
	public PropNode() {

	}
	
	private String value;
	public String getValue() { return value; }
	public void setValue(String v) { value = v; }
	
	public List<PropNode> children = new ArrayList<PropNode>();
	public List<PropNode> getChildren() { return children; }
	public PropNode createChild() {
		PropNode pn = new PropNode();
		children.add(pn);
		pn.parent = this;
		return pn;
	}
	private PropNode parent = null;

	public String getKey() {
		if (parent == null) return "";
		if (parent.name == null) return name;
		return parent.getKey() + "." + name;
	}
	
	public static String dump() {
		Buffer buf = new Buffer();
		buf.ln("# dump root PropNode");
		for (PropNode pn : root.children) {
			pn.dump(buf);
		}
		return buf.toString();
	}
	public void dump(Buffer buf) {
		buf.ind();
		buf.add(name);
		if (value != null) {
			buf.add(":");
			buf.add(" ");
			buf.add(value);
		}
		buf.nl();
		if (desc != null) {
			for (String line : desc.split("\n")) {
				buf.ln("  " + line);
			}
		}
		for (PropNode pn : children) {
			buf.incrIndent();
			pn.dump(buf);
			buf.decrIndent();
		}
	}

	public static void evaluate() { root.eval(); }
	public void eval() {
		put(this);
		for (PropNode pn : children) pn.eval();
	}
	
	
	private static PropNode root = new PropNode();

	//private static Pattern rx = Pattern.compile("(\\w+)");
	private static Pattern rx2 = Pattern.compile("(\\w+):\\s*(.*)");
	private static Pattern rx3 = Pattern.compile("(.*)\\s+//(.*)");
	
	public static void parse(String filename) {
		SrcLine src = SrcLine.parse(filename);
		print("src:\n"+src);
		root.parse(src);
	}
	
	public void parse(SrcLine src) {
		// lines
		for (SrcLine sl : src.getLines()) {
			String line = sl.getLine();
			//print("pline: "+ line);
			Matcher m = rx2.matcher(line);
			if (m.matches()) {
				// new node
				PropNode next = createChild();
				next.setName(m.group(1));
				String val = m.group(2);
				m = rx3.matcher(val);
				if (m.matches()) {
					next.setValue(m.group(1).trim());
					next.addDescLine(m.group(2).trim());
				} else {
					next.setValue(val.trim());
				}
				next.parse(sl);
			} else {
				// description
				if (parent != null) {
					addDescLine(line);
				}
			}
		}
	}
	
	private static int linenum = 0;
	
	private static Map<String,String> props = new HashMap<String,String>();
	public static String get(String key) { return props.get(key); }
	public static void put(PropNode n) {
		if (n.value == null) return;
        Pattern p = Pattern.compile("\\$\\(([\\w\\.]+)\\)");
        Matcher m = p.matcher(n.value);
        StringBuffer sb = new StringBuffer();
        while (m.find()) {
            String varname = m.group(1);
            if (!props.containsKey(varname)) fail("props key not found: " + varname);
            String value = get(varname);
            m.appendReplacement(sb, value);
        }
        m.appendTail(sb);
        String value = sb.toString();
		props.put(n.getKey(), value);
		print("put: " + n.getKey() + "=" + value);
	}
	
	public static void main(String args[]) {
		if (args.length < 2) fail("invalid num of args.\nUsage: main n.props [n2.props...] jugile.properties");
		for (int i = 0; i < args.length-1; i++) {
			PropNode.parse(args[i]);
		}
		PropNode.evaluate();
		Blob b = new Blob();
		List<String> keys = new ArrayList<String>(props.keySet());
		Collections.sort(keys);
		for (String key : keys) {
			b.append(""+key+"="+props.get(key)+"\n");
		}
		b.write(args[args.length-1]);
	}
	
}
